package great.project.easyexcel.easyexcel.util;

import com.alibaba.excel.EasyExcelFactory;
import com.alibaba.excel.support.ExcelTypeEnum;
import great.project.easyexcel.easyexcel.bo.ExcelErrorMessage;
import great.project.easyexcel.easyexcel.bo.ExcelProgress;
import great.project.easyexcel.easyexcel.listener.ExcelEventListListener;
import great.project.easyexcel.easyexcel.listener.SFunction;
import great.project.easyexcel.mock.ProcessMock;
import great.project.common.http.Result;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.stereotype.Component;
import org.springframework.web.multipart.MultipartFile;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.net.URLEncoder;
import java.util.List;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ExecutorService;
import java.util.function.Consumer;

/**
 * <div  style="border:2px solid none;margin:20px;padding:15px;font-size:24px;text-align:center;color:aqua;">
 * <p>
 *  excel 异步导入
 *
 *  </div>
 *
 * @author zhangshun
 * @date 2020/11/30 14:55
 */
@Component
public class ExcelUtils {

    @Resource
    ProcessMock processMock;

    @Resource(name = "excelExecutorService")
    private ExecutorService executorService;

    /**
     * 异步导入数据
     *
     * @param consumer         处理导入的数据（一般直接批量插入到数据库）
     * @param fixData          （补充数据，一般那情况下导入的数据只有excel上携带的数据， 该功能是补充excel上没有的数据）
     * @param validateFunction 验证参数 第一个是 当前的数据， 第二个是 当错误时 往里扔到错误信息
     * @param clazz            类型
     * @param multipartFile    需要导入的文件
     * @param <T>              泛型
     * @return 当前导入的key
     */
    public <T> Result<String> asyncImport(
            Consumer<List<T>> consumer
            , Consumer<T> fixData
            , SFunction<T, Set<Object>, ExcelErrorMessage, Boolean> validateFunction
            , Class<T> clazz
            , MultipartFile multipartFile
    ) {

        String uuid = UUID.randomUUID().toString();

        /*
         *  当前sheet一共有多少行数据
         */
        XSSFWorkbook sheets;
        try {
            sheets = new XSSFWorkbook(multipartFile.getInputStream());
        } catch (IOException e) {
            e.printStackTrace();
            return Result.fail("请传入正确的 .xlsx 文件");
        }

        int lastRowNum = sheets.getSheetAt(0).getLastRowNum();

        // 初始化 导入进度
        final ExcelProgress build = ExcelProgress.builder().now(0).total(lastRowNum).error(0).isBreak((byte) 0).build();
//        redisTemplate.opsForValue().set("EXCEL_PROCESS:" + uuid, build, 10, TimeUnit.MINUTES);
        processMock.updateProcess(uuid, build);

        final InputStream inputStream;
        try {
            inputStream = multipartFile.getInputStream();
        } catch (IOException e) {
            return Result.fail("读取文件异常");
        }

        executorService.execute(() -> {
            try {
                EasyExcelFactory.read(
                        inputStream,
                        clazz,
                        new ExcelEventListListener<>(
                                consumer, fixData, validateFunction
                                , processMock, lastRowNum, uuid
                        )
                )
                        .sheet()
                        .headRowNumber(1)
                        .doRead();
            } catch (Exception e) {

                e.printStackTrace();

                // 记录异常并且 更新异常状态
                final ExcelProgress excelProgress = ExcelProgress.builder().isBreak((byte) 0).breakMessage(e.getMessage()).build();
                processMock.updateProcess(uuid, excelProgress);

            }
        });

        return Result.success(uuid);
    }

    /**
     * 简单的 excel 导出
     *
     * @param response  响应
     * @param excelName excel名字
     * @param clazz     导出的类型
     * @param list      需要导出的数据
     * @param <T>       泛型
     */
    public static <T> void simpleExport(HttpServletResponse response, String excelName, Class<T> clazz, List<T> list) {
        // 这里注意 有同学反应使用swagger 会导致各种问题，请直接用浏览器或者用postman
        response.setContentType("application/vnd.ms-excel");
        response.setCharacterEncoding("utf-8");
        // 这里URLEncoder.encode可以防止excel的名字中文乱码 当然和easyexcel没有关系
        String fileName = null;
        try {
            fileName = URLEncoder.encode(excelName, "UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        response.setHeader("Content-disposition", "attachment;filename=" + fileName + "-" + System.currentTimeMillis() + ".xlsx");

        try {
            EasyExcelFactory
                    .write(response.getOutputStream(), clazz)
                    .excelType(ExcelTypeEnum.XLSX)
                    .sheet(excelName)
                    .doWrite(list);
        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    public static <T>  List<T> simpleImport(MultipartFile file, Class<T> clazz) throws IOException {
        return EasyExcelFactory.read(file.getInputStream())
                .head(clazz)
                .doReadAllSync();
    }


    /**
     * 查询处理进度
     *
     * @param processKey 处理的key
     * @return 结果
     */
    public Result<ExcelProgress> checkProcess(String processKey) {
        ExcelProgress process = processMock.getProcess(processKey);
        if (process == null) {
            return Result.fail("暂无数据");
        }
        return Result.success(process);
    }


    public List<ExcelErrorMessage> getErrorMessage(String processKey) {
        return processMock.getErrorMessages(processKey);
    }

}
